package com.mrx.spring.example.service;

import com.mrx.commons.annotations.AfterCommit;
import com.mrx.commons.trace.TraceUtils;
import com.mrx.spring.example.dao.TestMapper;
import com.mrx.spring.example.runner.TraceIdRunner;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * @author Mr.X
 * @since 2023-11-10 16:40
 **/
@Service
public class TestService {

    private static final Logger logger = LoggerFactory.getLogger(TestService.class);

    @Resource
    private TraceIdRunner traceIdRunner;

    @Resource
    private TestMapper testMapper;

    @Lazy
    @Resource
    private TestService testService;

    @EventListener(ApplicationReadyEvent.class)
    public void initTable() {
        logger.info("initTable");
        testMapper.initTable();
    }

    @Async("traceExecutor")
    public void asyncTestTask() {
        logger.info("AsyncTestTask: {}", TraceUtils.getTraceId());
        traceIdRunner.init(null);
    }

    public void addTest(Integer id) {
        testMapper.insert(id);
        logger.info("插入数据后, 当前数据库所有数据: {}", testMapper.listAll());
    }

    @AfterCommit
    @SneakyThrows
    public void afterCommit(Integer id) {
        // sleep 3s, 确保事务结束
        TimeUnit.SECONDS.sleep(3);
        String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
        logger.info("running transaction: {}, transactionName: {}", id, transactionName);
        logger.info("after transaction: {}", testMapper.listAll());
    }

    @Transactional(rollbackFor = Throwable.class)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void afterCommitListener(Integer id) {
        String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
        logger.info("running transaction in listener: {}, transactionName: {}", id, transactionName);
        logger.info("after transaction: {}", testMapper.listAll());
        logger.info("新事务");
        testMapper.insert(id);
        logger.info("查询结果: {}", testMapper.listAll());
    }

    @Async("traceExecutor")
    @Transactional(rollbackFor = Throwable.class)
    public Future<?> asyncTransaction(Integer id) {
        logger.debug("事务开始, 现在的数据为: {}", testMapper.listAll());
        IntStream.rangeClosed(1, id).forEach(testMapper::insert);
        logger.debug("新增后的数据为: {}", testMapper.listAll());
        throw new RuntimeException("测试回滚异常辣");
    }

    @Transactional(rollbackFor = Throwable.class)
    public void newTransaction(Integer id) {
        logger.debug("最里层事务开始, 当前数据为: {}", testMapper.listAll());
        testMapper.insert(id);
        logger.debug("新增后的数据为: {}", testMapper.listAll());
        throw new RuntimeException("测试回滚异常辣");
    }

    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW)
    public void newTransaction2(Integer id) {
        // Transactional 注解的 propagation 表示执行被标识方法时应该怎么开启事务, 如果是嵌套事务需要捕获异常的情况
        // 则应该在里层事务将其设置为 Propagation.REQUIRES_NEW
        logger.debug("最里层事务开始, 当前数据为: {}", testMapper.listAll());
        testMapper.insert(id);
        logger.debug("新增后的数据为: {}", testMapper.listAll());
        throw new RuntimeException("测试回滚异常辣");
    }

}
